package com.lagou.module2.task2;

import java.util.Scanner;

/**
 * 实现控制台版并支持两人对战的五子棋游戏，棋盘大小（包括边界标识）为{@value MAX_ROW_COUNT} X {@value MAX_COL_COUNT}. 也就是16X16
 *
 * 五子棋游戏规则如下：
 * （1）对局双方各执一色棋子
 * （2）空棋盘开局
 * （3）黑先，白后，交替下子，每次只能下一子
 * （4）棋子下在棋盘的空白点上，棋子下定后，不得向其它点移动，不得从棋盘上拿掉或拿起另落别处
 * （5）黑方的第一枚棋子可下在棋盘任意交叉点上
 * @author changyongfeng
 * @create 2021-06-19 15:43
 *
 */

public class ChessGame {

    // 声明静态常量，常量一般大写
    private static final int MAX_ROW_COUNT = 17;  // 16行
    private static final int MAX_COL_COUNT = 17;  // 16列
    private static final char BLACK_CHAR; // 黑棋子
    private static final char WHITE_CHAR; // 白棋子
    private static final char BOARD_EMPTY_MARK; // 空白点

    /**
     * 如果为true，则黑方下棋；为false，则白方下棋
     * @param args
     */
    private boolean flag = true;

    /**
     * 采用二维数组来模拟五子棋的棋盘，并采用private和final修饰
     * @param args
     */
    private final char[][] chessBoard;

    /**
     * 下棋回合计数器，统计下了多少回合
     * @param args
     */
    private int cnt;

    // 构造方法
    public ChessGame(){}

    // 静态代码块，初始化静态常量，与类同级，随类的加载而加载
    static {
        // 黑色棋子用数字1标识
        BLACK_CHAR = 9608;
        // 白色棋子用2标识
        WHITE_CHAR = 9617;
        // 空白点有+标识
        BOARD_EMPTY_MARK = 43;
    }

    // 构造块，初始化变量和二维数组
    {
        chessBoard = new char[MAX_ROW_COUNT][MAX_COL_COUNT];
        cnt = 0;
    }

    /**
     * 更新指令，直接取反变量
     */
    public void updateFlag(){
        this.flag = !this.flag;
    }

    // gatter
    public boolean isFlag(){
        return flag;
    }

    // setter
    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    /**
     * 根据用户输入的位置信息，放置棋子
     * @param y 横坐标
     * @param x 纵坐标
     * @return 放置棋子是否成功
     */
    public boolean setChessBoard(int y, int x){
        // 判断位置是否有棋子
        if (chessBoard[y][x] != BOARD_EMPTY_MARK){
            System.out.println("这个位置已经有棋子了，请重新输入");
            System.out.println();
            return false;
        }

        // 判断棋子颜色
        if (flag){
            chessBoard[y][x] = BLACK_CHAR;  // 下黑色棋子
        }else{
            chessBoard[y][x] = WHITE_CHAR; // 下白色棋子
        }
        return true;
    }

    /**
     * 判断此玩家本回合是否获胜
     * 四个方向遍历检查：上下，左右，右上左下，左上右下
     * @param y 玩家本回合棋子的纵坐标
     * @param x 玩家本回合棋子的横坐标
     * @return 返回玩家本回合是否获胜
     */
    public boolean checkPlayerStatus(int y, int x){
        // 初始化检查，检查边界值
        int minY = y - 4, minX = x - 4, maxY = y + 4, maxX = x + 4;
        minY = Math.max(minY, 1); minX = Math.max(minX, 1);
        maxY = Math.min(maxY, 16); maxX = Math.min(maxX, 16);

        // 初始化计数器和下标变量
        int counter, i , j;

        // 上下遍历； 初始化计数器为1， 代表本回合的棋子
        counter = 1;
        // 往上循环
        i = y - 1;
        j = x;
        while (i >= minY){
            if (chessBoard[y][x] == chessBoard[i][j]){
                counter++;
            }else{
                break;
            }
            i--;
        }
        // 往下循环（以本回合棋子的值为对比，有一个不相等，则跳出循环）
        i = y + 1;
        j = x;
        while (i <= maxY){
            if (chessBoard[y][x] == chessBoard[i][j]){
                counter++;
            }else{
                break;
            }
            i++;
        }
        // 检查是否获胜
        if (counter >= 5){
            return true;
        }

        // 右上左下遍历；初始化计数器为1， 代表本回合的棋子
        counter = 1;
        // 往右上循环（以本回合棋子的值为对比，有一个不相等，则退出循环）
        i = y - 1;
        j = x + 1;
        while (i >= minY && j <= maxX){
            if (chessBoard[y][x] == chessBoard[i][j]){
                counter++;
            }else{
                break;
            }
            i--;
            j++;
        }
        // 往左下循环（以本回合棋子的值为对比，有一个不相等，则退出循环）
        i = y + 1;
        j = x - 1;
        while (i >= maxY && j <= minX){
            if (chessBoard[y][x] == chessBoard[i][j]){
                counter++;
            }else{
                break;
            }
            i++;
            j--;
        }
        // 检查是否获胜
        if (counter >= 5){
            return true;
        }

        // 左上右下遍历；初始化计数器为1， 代表本回合的棋子
        counter = 1;
        // 往右下循环（以本回合棋子的值为对比，有一个不相等，则退出循环）
        i = y + 1;
        j = x + 1;
        while (i >= maxY && j <= maxX){
            if (chessBoard[y][x] == chessBoard[i][j]){
                counter++;
            }else{
                break;
            }
            i++;
            j++;
        }
        // 往左上循环（以本回合棋子的值为对比，有一个不相等，则退出循环）
        i = y - 1;
        j = x - 1;
        while (i >= minY && j <= minX){
            if (chessBoard[y][x] == chessBoard[i][j]){
                counter++;
            }else{
                break;
            }
            i--;
            j--;
        }
        // 检查是否获胜
        if (counter >= 5){
            return true;
        }

        // 本回合玩家没有获胜
        return false;
    }

    /**
     * 模拟把棋子放置棋盘的过程
     * @return 本回合玩家是否获胜
     */
    public boolean playChess(){
        if (isFlag()){
            System.out.println("黑方回合：");
        }else{
            System.out.println("白方回合：");
        }
        //模拟用户的输入
        Scanner sc = new Scanner(System.in);
        int y = 0, x = 0;
        try {
            y = Integer.parseInt(sc.next().trim(), 16);
            x = Integer.parseInt(sc.next().trim(), 16);
            // 判断用户输入的位置坐标是否超出限制范围
            if (y < 0 || y > 15 || x < 0 || x > 15) {
                System.out.println("错误：输入值超过范围，请重新输入 0 到 f 之间的值");
                System.out.println("0 1 2 3 4 5 6 7 8 9 a b c d e f");
            } else {
                // 判断获胜
                y++;
                x++;
                if (setChessBoard(y, x)) {
                    //打印输出
                    cnt++;
                    System.out.println("第" + cnt + " 回合");
                    outputBoard();
                    System.out.println();

                    // 检测玩家是否获胜
                    if (checkPlayerStatus(y, x)) {
                        return true;
                    }

                    // 更新回合
                    updateFlag();
                    return false;
                }
            }
        }catch (NumberFormatException e){
            System.out.println();
            System.out.println("错误：输入字符不合法，请重新输入 0 到 f 之间的值，如下所示：");
            System.out.println("0 1 2 3 4 5 6 7 8 9 a b c d e f");
            System.out.println();
        }
        return false;
    }

    /**
     * 输出绘制棋盘
     *
     */
    public void outputBoard(){
        for (int i = 0; i<chessBoard.length;i++){
            for (int j = 0; j < chessBoard[i].length;j++){
                System.out.print(chessBoard[i][j] + " ");
            }
            System.out.println();
        }
    }

    /**
     * 初始化棋盘，并绘制输出到控制台
     *
     */
    public void initializeChessBoard(){
        // 空格的ASCII 为32
        chessBoard[0][0] = 32;

        for (int i = 1; i< chessBoard.length; i++){
            //添加0 到 f的符号
            chessBoard[0][i] = Integer.toHexString(i - 1).charAt(0);
            chessBoard[i][0] = Integer.toHexString(i - 1).charAt(0);
            //添加 '0' 符号
            for (int j = 1; j< chessBoard[i].length; j++){
                chessBoard[i][j] = BOARD_EMPTY_MARK;
            }
        }
        //绘制输出到控制台
        outputBoard();
    }

    public static void main(String[] args) {
        // 创建对象
        ChessGame chessGame = new ChessGame();

        System.out.println("初始化五子棋棋盘：");
        //（1）成员方法一：实现绘制棋盘
        chessGame.initializeChessBoard();
        System.out.println();

        // 黑棋先行，白棋后行， true表示是黑棋回合
        chessGame.setFlag(true);

        System.out.println("游戏开始， 请黑白双方分别输入坐标 -- 先输入纵坐标，后输入横坐标， 用空格隔开");
        System.out.println();

        // 循环模拟黑白双方交替下棋的过程
        for (;;){
            //（2）成员方法二：实现提示黑方和白方分别下棋并重新绘制棋盘
            if (chessGame.playChess()){
                System.out.println("恭喜" + (chessGame.isFlag() ? "黑方" : "白方") + "获胜！");
                System.out.println("游戏结束，再见！");
                break;
            }
        }

    }
}
